Your quirky boss collects rare, old coins...
They found out you're a programmer and asked you to solve something they've been wondering for a long time.
Write a function that, given:
computes the number of ways to make the amount of money with coins of the available denominations.
Example: for amount= (¢) and denominations= (¢, ¢ and ¢), your program would output —the number of ways to make ¢ with those denominations:
We use a bottom-up algorithm to build up a table ways_of_doing_n_cents such that ways_of_doing_n_cents[k] is how many ways we can get to k cents using our denominations. We start with the base case that there's one way to create the amount zero, and progressively add each of our denominations.
The number of new ways we can make a higher_amount when we account for a new coin is simply ways_of_doing_n_cents[higher_amount - coin], where we know that value already includes combinations involving coin (because we went bottom-up, we know smaller values have already been calculated).
def change_possibilities_bottom_up(amount, denominations):
ways_of_doing_n_cents = [0] * (amount + 1)
ways_of_doing_n_cents[0] = 1
for coin in denominations:
for higher_amount in range(coin, amount + 1):
higher_amount_remainder = higher_amount - coin
ways_of_doing_n_cents[higher_amount] += (
ways_of_doing_n_cents[higher_amount_remainder]
)
return ways_of_doing_n_cents[amount]
Here's how ways_of_doing_n_cents would look in successive iterations of our function for amount= and denominations=.
===========
key:
a = higher_amount
r = higher_amount_remainder
===========
============
for coin = 1:
============
[1, 1, 0, 0, 0, 0]
r a
[1, 1, 1, 0, 0, 0]
r a
[1, 1, 1, 1, 0, 0]
r a
[1, 1, 1, 1, 1, 0]
r a
[1, 1, 1, 1, 1, 1]
r a
============
for coin = 3:
=============
[1, 1, 1, 2, 1, 1]
r a
[1, 1, 1, 2, 2, 1]
r a
[1, 1, 1, 2, 2, 2]
r a
============
for coin = 5:
=============
[1, 1, 1, 2, 2, 3]
r a
final answer: 3
time and additional space, where is the amount of money and is the number of potential denominations.
This question is in a broad class called "dynamic programming." We have a bunch more dynamic programming questions we'll go over later.
Dynamic programming is kind of like the next step up from greedy. You're taking that idea of "keeping track of what we need in order to update the best answer so far," and applying it to situations where the new best answer so far might not just have to do with the previous answer, but some other earlier answer as well.
So as you can see in this problem, we kept track of all of our previous answers to smaller versions of the problem (called "subproblems") in a big list called ways_of_doing_n_cents.
Again, same idea of keeping track of what we need in order to update the answer as we go, like we did when storing the max product of 2, min product of 2, etc in the highest product of 3 question. Except now the thing we need to keep track of is all our previous answers, which we're keeping in a list.
We built that list bottom-up, but we also talked about how we could do it top-down and memoize. Going bottom-up is cleaner and usually more efficient, but often it's easier to think of the top-down version first and try to adapt from there.
Dynamic programming is a weak point for lots of candidates. If this one was tricky for you, don't fret. We have more coming later.
Do you have an answer?
Wanna review this one again later? Or do you feel like you got it all?
Mark as done Pin for review laterReset editor
Powered by qualified.io